
#include <pthread.h>
#include <cstring>
#include <iostream>

#include "contest_interface.h"
#include "access/BufferManager.h"
#include "access/DatabaseManager.h"
#include "access/Transaction.h"


using namespace std;

#define MAX_INDEX_NUM 32
#define MAX_INDEX_NAME_LEN 128

#define _DEBUG_TRANS_

typedef struct Index 
{
	void* db_pointer;
}Index;


pthread_mutex_t root_mutex = PTHREAD_MUTEX_INITIALIZER;

BufferManager bmManager(10, 2, 16);
DatabaseManager dbManager(&bmManager);

ErrorCode BeginTransaction(Transaction **tx)
{

	*tx = new Transaction();

#ifdef _DEBUG_TRANS_
    cout << "Begin Transaction "  << endl;
#endif
        
    return kOk;
}

ErrorCode AbortTransaction(Transaction **tx)
{
    if(!tx)
        return kTransactionAborted;

#ifdef _DEBUG_TRANS_
    cout << "Abort Transaction " << endl;
#endif

        
	(*tx)->Abort();

    delete *tx;
    
    return kOk;
}


ErrorCode CommitTransaction(Transaction **tx)
{
    if(!tx)
        return kTransactionAborted;

#ifdef _DEBUG_TRANS_
    cout << "Commit Transaction " << endl;
#endif

    (*tx)->Commit();

    delete *tx;
    
    return kOk;
}

ErrorCode CreateIndex(const char* name, uint8_t column_count, KeyType type, Transaction* trans)
{
	if(!trans)
		return kTransactionAborted;

    bool ret = dbManager.AddDatabase(name, column_count, type, trans);

	cout << "Database \"" << name << "\" created." << endl;
    
	if(ret)
		return kOk;
	return kErrorIndexExists;
}

ErrorCode OpenIndex(const char* name, Index **idx)
{
	*idx = new Index;

	(*idx)->db_pointer = dbManager.OpenDatabase(name);

	if((*idx)->db_pointer)
	{
		cout << "Database \"" << name << "\" opened" << endl;
		return kOk;	
	}
	cout << "Database " << name << " not found" << endl;
	return kErrorUnknownIndex;
}

ErrorCode InsertRecord(Transaction *tx, Index *idx, Record *record)
{
	if(!tx)
		return kTransactionAborted;

	if(!idx)
		return kErrorUnknownIndex;

    dbManager.InsertRecord(idx->db_pointer, record, tx);

	return kOk;
}

Record* CreateRecord(const int32_t k_1, const int64_t k_2, const char* k_3, const char* payload)
{
    cout << "Inserting new record ..." << endl;
    char* buffer = new char[1000];
    
    *((int32_t*)buffer) = k_1;
    *((int64_t*)(buffer + sizeof(int32_t))) = k_2;
    strcpy(buffer + sizeof(int32_t) + sizeof(int64_t), k_3);
    strcpy(buffer + sizeof(int32_t) + sizeof(int64_t) + MAX_VARCHAR_LENGTH, payload);
    
    return (Record*)buffer;
}

void PrintDatabase(Index *idx)
{
    dbManager.PrintDatabase(idx->db_pointer);
}

#define PRIMARY_INDEX "primary_index"
#define SECOND_INDEX "second_index"

int main(int argc, char* argv[])
{
	KeyType keys = {kShort,kInt,kVarchar};
	KeyType keys2 = {kInt, kInt, kInt, kShort};

	uint8_t dim = 3;
	uint8_t dim2 = 4;

    Transaction* t;
    
    BeginTransaction(&t);
    
	CreateIndex(PRIMARY_INDEX, dim, keys, t);
	
	CreateIndex(SECOND_INDEX, dim2, keys2, t);

	Index* i;

	CommitTransaction(&t);

	OpenIndex(PRIMARY_INDEX, &i); 

	//OpenIndex("hello world", &i);

    BeginTransaction(&t);

    Record* rec;
    
    rec = CreateRecord(1,12345,"first record", "index.h");
    InsertRecord(t, i, rec);
    delete [] (char*)rec;

    rec = CreateRecord(2,5432100,"second record", "BufferManager.h");
    InsertRecord(t, i, rec);
    delete [] (char*)rec;

    rec = CreateRecord(3,5222100,"third record", "Page.cpp");
    InsertRecord(t, i, rec);
    delete [] (char*)rec;

 
    rec = CreateRecord(4,5222100,"fourth record", "DatumPage.cpp");
    InsertRecord(t, i, rec);
    delete [] (char*)rec;
    
	CommitTransaction(&t);

	//AbortTransaction(&t);

    PrintDatabase(i);
    
    delete i;
    
	return 0;
}
